
// ============================================================================
// Data mapping
// ============================================================================

// Use the global memory as the task queue
const Queue* taskQueue = (void*) shared_mem_pt;

// Use rdma0 as a temporary slot
DMA dma0 = {1, (void*) dma0_BASEADDR};
Slot* tempSlot0 = (void*) cmemout0_BASEADDR;

// Use rdma1 as a temporary slot state
DMA dma1 = {2, (void*) dma1_BASEADDR};
SlotState* stateTemp = (void*) cmemout1_BASEADDR;

// Use synchronization register as queue state
TileQueueState* stateTileQueue = (void*) cmemin0_BASEADDR;

// Use tile 2 synchronization registers as queue state
const QueueState* stateQueue = (void*) mb2_cmemin0_pt;

// Use Dmem as a local buffer
Row localBuffer[7];

// ============================================================================
// Transfer functions
// ============================================================================

void copyRows(Row* source, Row* destination, int numberOfRows) {

	memcpy(destination, source, sizeof(Row) * numberOfRows);
}

void receiveRows(Row* buffer, DMA* dma, int slot) {

	hw_dma_receive((void*) buffer, (void*) &taskQueue->tiles[TILE_ID].slots[slot], sizeof(Slot) / 4, dma);
}

void sendRows(Row* buffer, DMA* dma, int slot, int numberOfRows) {

	hw_dma_send((void*) &taskQueue->tiles[TILE_ID].slots[slot], (void*) buffer, sizeof(Slot) / 4, dma);
}

// Write slot state
void updateQueueSlot(int slot, SlotState newState) {

	*stateTemp = newState;
	hw_dma_send((void*) &stateQueue->tiles[TILE_ID].slots[slot], (void*) stateTemp, sizeof(SlotState) / 4, &dma1);

	stateTileQueue->slots[slot] = newState;
}

// ============================================================================
// Main entry point
// ============================================================================

int main() {

	// Ensure platform initialisation has completed
	mon_sync(0);

	print_debug(1);	
	
	// This tile starts filtering at line 0.
	int done = 0;

	int line = 0;

	while(!done) {

		int i;
		for (i = 0; i < QUEUE_LENGTH; i++) {

			SlotState newState;

			switch(stateTileQueue->slots[i].state) {

			case STATE_START:

				print_debug(2);

				line = stateTileQueue->slots[i].startRow;

				receiveRows(&tempSlot0->rows[0], &dma0, i);
				copyRows(&tempSlot0->rows[0], &localBuffer[2], 5);

				filterRow(&localBuffer[2], &localBuffer[3], &localBuffer[4], &tempSlot0->rows[2], line + 1);
				filterRow(&localBuffer[3], &localBuffer[4], &localBuffer[5], &tempSlot0->rows[3], line + 2);
				filterRow(&localBuffer[4], &localBuffer[5], &localBuffer[6], &tempSlot0->rows[4], line + 3);

				sendRows(&tempSlot0->rows[2], &dma0, i, 3);

				copyRows(&localBuffer[5], &localBuffer[0], 2);

				newState.state = STATE_PROCESSED;
				newState.startRow = line + 1;
				newState.numRows = 3;
				updateQueueSlot(i, newState);

				line += 5;

				break;

			case STATE_STOP:
			case STATE_UNPROCESSED:
			
				if (stateTileQueue->slots[i].startRow != line){					
					break;
				}

				if (stateTileQueue->slots[i].state == STATE_STOP) {
					done = 1;
				}

				print_debug(3);

				receiveRows(&tempSlot0->rows[0], &dma0, i);
				copyRows(&tempSlot0->rows[0], &localBuffer[2], 5);
							
				filterRow(&localBuffer[0], &localBuffer[1], &localBuffer[2], &tempSlot0->rows[0], line - 1);
				filterRow(&localBuffer[1], &localBuffer[2], &localBuffer[3], &tempSlot0->rows[1], line + 0);
				filterRow(&localBuffer[2], &localBuffer[3], &localBuffer[4], &tempSlot0->rows[2], line + 1);
				filterRow(&localBuffer[3], &localBuffer[4], &localBuffer[5], &tempSlot0->rows[3], line + 2);
				filterRow(&localBuffer[4], &localBuffer[5], &localBuffer[6], &tempSlot0->rows[4], line + 3);
				
				sendRows(&tempSlot0->rows[0], &dma0, i, 5);

				copyRows(&localBuffer[5], &localBuffer[0], 2);

				newState.state = STATE_PROCESSED;
				newState.startRow = line - 1;
				newState.numRows = 5;
				updateQueueSlot(i, newState);

				line += 5;

				break;
			}
		}
	}

	return 0;
}
